Load Packages

First I need to load up the packages I’ll need

library(sf)
library(ggplot2) #development version!
## devtools::install_github("tidyverse/ggplot2")
library(tidyverse)
library(readr)
## Not sure about this bit
#library("tidyverse",lib.loc="/Library/Frameworks/R.framework/Versions/3.4/Resources/library")
library(cowplot)
library(sp)
library(gridExtra)
library(dplyr)

Import Data

Now I import my data. I filter for the Arran postcodes, (since Arran all begins ‘KA27’).

## Finding the Arran coordinates
allcoordinates <- read.csv("alldata/ukpostcodes.csv")
arrancoordinates <- filter(allcoordinates,substr(postcode,1,4)=="KA27")

Plot Coordinates

Now I plot these coordinates.

## Plotting the Arran coordinates
ggplot(data = arrancoordinates) +
  geom_point(mapping = aes(x = longitude, y = latitude), shape=20) +
  ggtitle("Arran Postcodes") +
  labs(title = "Arran Postcodes", x = "Longitude", y = "Latitude") +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme_grey() +
  coord_map()

Now I create some plots. #Arran Borders

pcs <- read_sf("alldata/Scotland_pcs_2011")
#Print Post codes lists
arransubsect <- filter(pcs,substr(label,1,4)=="KA27")
arransubsect %>%
  ggplot() +
  geom_sf() +
  theme(axis.text.x=element_text(angle=45, hjust = 1)) +
  theme_grey()

Simple.sf

After a little editing I can overlay the two.

simple.sf <- st_as_sf(arrancoordinates, coords=c('longitude','latitude'))
st_crs(simple.sf) <- 4326
simple.sf %>% 
  ggplot() + 
  geom_sf(shape=20) +
  theme_grey() 

arransubsect %>%
  ggplot() +
  geom_sf() +
  theme(axis.text.x=element_text(angle=45, hjust = 1)) +
  theme_grey() +
  geom_sf(data=simple.sf, shape=20)

Shape files

Now I load the SIMD data, containing the geometries (shapefiles) and SIMD data (percentiles, etc)

#Import SIMD data from http://www.gov.scot/Topics/Statistics/SIMD
#https://data.gov.uk/dataset/scottish-index-of-multiple-deprivation-simd-2012
#https://data.gov.uk/dataset/scottish-index-of-multiple-deprivation-simd-2012/resource/d6fa8924-83da-4e80-a560-4ef0477f230b
DZBoundaries2016 <- read_sf("./alldata/SG_SIMD_2016")
DZBoundaries2012 <- read_sf("./alldata/SG_SIMD_2012")
DZBoundaries2009 <- read_sf("./alldata/SG_SIMD_2009")
DZBoundaries2006 <- read_sf("./alldata/SG_SIMD_2006")
DZBoundaries2004 <- read_sf("./alldata/SG_SIMD_2004")

Select Arran SIMD data

I have to choose the right columns manually in order to select the Arran data.

#Selecting Arran data from Scotland (2016)
#Find postcode look-up from below file for KA27 postcodes. Find unique DZ. Find row positions.
#SIMD2016 <-read.csv("./alldata/00505244.csv")
#Selecting ArranDZ2016
Arrandz2016 <- c(4672,4666,4669,4671,4667,4668,4670)
arran2016 <- DZBoundaries2016[Arrandz2016,]
#Find postcode look-up, KA27 postcodes. Find unique DZ. Find row positions.
#Selecting ArranDZ2012
Arrandz2012 <- c(4409,4372,4353,4352,4351,4350,4349)
#2012
arran2012 <- DZBoundaries2012[Arrandz2012,]
#2009
arran2009 <- DZBoundaries2009[Arrandz2012,]
#2006
arran2006 <- DZBoundaries2006[Arrandz2012,]
#2004
arran2004 <- DZBoundaries2004[Arrandz2012,]

Now I want to plot all the data, first I combine it all into one table. First I subselect the data I want from the appropriate columns.

arran20162 <- arran2016 %>%
  select(DataZone, geometry, Percentile)  %>%
  mutate(year="2016")
arran20122 <- arran2012 %>%
  select(DataZone, geometry, Percentile) %>%
  mutate(year="2012")
arran20092 <- arran2009 %>%
  select(DataZone, geometry, Percentile) %>%
  mutate(year="2009")
arran20062 <- arran2006 %>%
  select(DataZone, geometry, Percentile) %>%
  mutate(year="2006")
arran20042 <- arran2004 %>%
  select(DataZone, geometry, Percentile) %>%
  mutate(year="2004")
arransimd <- rbind(arran20162,arran20122,arran20092,arran20062,arran20042)

The reason I’ve downloaded all the datazones shapefiles individually is because they change between 2016 and 2012. See the small differences.

arran1612 <- rbind(arran20162,arran20122)
arran1612 %>%
  ggplot() +
  geom_sf(aes(fill = DataZone)) +
  facet_wrap('year') +
  theme_grey() +
  theme(legend.position="none") +
  theme(axis.text.x=element_text(angle=45, hjust = 1))

Arran Percentile Plots

Now I plot the percentiles.

arransimd %>%
  ggplot() +
  geom_sf(aes(fill = Percentile)) +
  facet_wrap('year') +
  theme_grey() +
  theme(axis.text.x=element_blank(),
        axis.ticks.x=element_blank(), axis.text.y=element_blank(),
        axis.ticks.y=element_blank())

arransimd %>%
  ggplot() +
  geom_sf(aes(fill = Percentile)) +
  facet_wrap('year', nrow = 1) +
  theme_grey() +
  theme(axis.text.x=element_blank(),
        axis.ticks.x=element_blank(), axis.text.y=element_blank(),
        axis.ticks.y=element_blank())

There we are. Not the SIMD health percentiles of Arran zones throughout SIMD history. And I’ve learned a little bit about graphics in R.

If I wanted to I could show the zones individually.. First I find the unique zones. (There are 14. 7 Zones 2016, 7 Zones pre-2016)

datazones <- unique(arransimd$DataZone)
datazones
 [1] "S01011177" "S01011171" "S01011174" "S01011176" "S01011172" "S01011173" "S01011175" "S01004409" "S01004372" "S01004353"
[11] "S01004352" "S01004351" "S01004350" "S01004349"

I’ll have to find out a simpler way to do this but.. #Pre-2016 Individual Zones

function0.5 <- function(argument) 
{
  filter(arransimd, DataZone==argument)
}

Ways to create individual shapefiles

filter(arransimd, DataZone=="S01004353")
Simple feature collection with 4 features and 3 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: 186117 ymin: 618919 xmax: 206903.5 ymax: 652363.5
epsg (SRID):    NA
proj4string:    +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +datum=OSGB36 +units=m +no_defs
   DataZone Percentile year                       geometry
1 S01004353         78 2012 MULTIPOLYGON(((200930 63676...
2 S01004353         68 2009 MULTIPOLYGON(((200930 63676...
3 S01004353         71 2006 MULTIPOLYGON(((200930 63676...
4 S01004353         65 2004 MULTIPOLYGON(((200930 63676...
function0.5("S01004353")
Simple feature collection with 4 features and 3 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: 186117 ymin: 618919 xmax: 206903.5 ymax: 652363.5
epsg (SRID):    NA
proj4string:    +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +datum=OSGB36 +units=m +no_defs
   DataZone Percentile year                       geometry
1 S01004353         78 2012 MULTIPOLYGON(((200930 63676...
2 S01004353         68 2009 MULTIPOLYGON(((200930 63676...
3 S01004353         71 2006 MULTIPOLYGON(((200930 63676...
4 S01004353         65 2004 MULTIPOLYGON(((200930 63676...
pre2016list2 <- list("S01004409", "S01004372", "S01004353", "S01004352", "S01004351", "S01004350", "S01004349")
#Create a new way of making character list
pre2016list <- lapply(pre2016list2, function0.5)
function1 <- function(argument) 
{
  argument %>%
  ggplot() +
  geom_sf(aes(fill = Percentile)) +
  facet_wrap('year') +
  theme_grey() +
  theme(axis.text.x=element_blank(),
        axis.ticks.x=element_blank())
}
function2 <- function(argument) 
{
  arransubsect %>%
  ggplot() +
  geom_sf() +
  theme_grey() +
  theme(axis.text.x=element_text(angle=45, hjust = 1)) +
  geom_sf(data= argument, aes(fill = DataZone))
}
function3 <- function(argument) 
{
  argument %>%
  ggplot() +
  geom_sf(data = arransubsect) +
  geom_sf(aes(fill = Percentile)) +
  facet_wrap('year') +
  theme_grey() +
  theme(axis.text.x=element_blank(),
        axis.ticks.x=element_blank())
}
function4 <- function(argument) 
{
  plot_grid((function1(argument)), (function2(argument)), labels = c("A", "B"))
}

Apply function4 to all Pre-2016.

lapply(pre2016list, function4)
[[1]]

[[2]]

[[3]]

[[4]]

[[5]]

[[6]]

[[7]]

2016

Now I apply the same functions to the post-2016 Zones

arransubsect <- filter(pcs,substr(label,1,4)=="KA27")
#post2016list <- list(S01011177, S01011171, S01011174, S01011176, S01011172, S01011173, S01011175)
post2016list2 <- list("S01011177", "S01011171", "S01011174", "S01011176", "S01011172", "S01011173", "S01011175")
post2016list <- lapply(post2016list2, function0.5)
lapply(post2016list, function4)
[[1]]

[[2]]

[[3]]

[[4]]

[[5]]

[[6]]

[[7]]

Plot the percentiles as bar charts.

Pre-2016

arransimd2 <- dplyr::filter(arransimd, year < 2016)
arransimd2 %>%
ggplot(aes(x=year, y=Percentile)) +
  geom_bar(stat="identity") +
  facet_wrap('DataZone') +
  theme_grey() +
  labs(title = "Arran SIMD Datazones", x = "Year", y = "Percentile") +
  theme(plot.title = element_text(hjust = 0.5))

Post-2016

arransimd3 <- dplyr::filter(arransimd, year >= 2016)
arransimd3 %>%
ggplot(aes(x=year, y=Percentile)) +
  geom_bar(stat="identity") +
  facet_wrap('DataZone') +
  theme_grey() +
  labs(title = "Arran SIMD Datazones", x = "Year", y = "Percentile") +
  theme(plot.title = element_text(hjust = 0.5))

Now I want to overlay the postcodes for a particular shapefile, in this case by Datazone. To do this I’ve converted both the Arran coordinates and Arran (2016) shapefiles into Spatial Points/Polygons, converted them into a common CRS, and then compared them by using over().

exampleshapes <- sf:::as_Spatial(arran2016$geom)
examplepoints <- sf:::as_Spatial(simple.sf$geom)
examplepoints <- spTransform(examplepoints, CRS("+proj=longlat +datum=WGS84"))
exampleshapes <- spTransform(exampleshapes, CRS("+proj=longlat +datum=WGS84"))
namingdzpostcode <- over(exampleshapes, examplepoints, returnList = TRUE)

I can then take a member reference from the orginal postcode list, which gives me a selection of the rows in that DZ. For simplicity I’ve written this as a new function.

Unfortunately, I haven’t worked out how to coordinate the new ID with the original DZ names yet, so I have to select by using the appropriate ID for each DZ.

pre2016listID <- list(3,2,1,4,7,6,5)
post2016listID <- list(1,2,3,4,5,6,7)

Function selecting simple.sf by DZ ID.

function6 <- function(argument) 
{
  simple.sf[namingdzpostcode[[argument]],]
}

I can then use the above, and plot over the appropriate DZ shapefile. e.g #Projecting the coordinate selections #need to automate

function1(function0.5("S01004372")) +
  geom_sf(data=function6(2), shape=20)

function2(function0.5("S01004372")) +
  geom_sf(data=function6(2), shape=20)

function3(function0.5("S01004372")) +
  geom_sf(data=function6(2), shape=20)

If I edit function 4 a little so that the geom_sf layer is a second argument then I can also use function 4.

function4.5 <- function(argument, argument2) 
{
  a <- function1(argument)
  b <- function2(argument) +
  geom_sf(data=function6(argument2), shape=20)
  plot_grid(a, b, labels = c("A", "B"))
}
function4.5(function0.5("S01004372"),2)

I’ve also made another function to plot the DZ on it’s own with coordinates.

function5 <- function(argument, argument2) 
{
  argument %>%
  ggplot() +
  geom_sf() +
  theme_grey() +
  geom_sf(data=function6(argument2), shape=20) +
  theme(axis.text.x=element_blank(),
        axis.ticks.x=element_blank())
}
function5(function0.5("S01004372"),2)

function1.5 <- function(argument) 
{
  argument %>%
  ggplot() +
  geom_sf(aes(fill = Percentile)) +
  facet_wrap('year') +
  theme_grey() +
  theme(axis.text.x=element_blank(),
        axis.ticks.x=element_blank()) +
  theme(legend.position="bottom")  
}
function2.5 <- function(argument) 
{
  arransubsect %>%
  ggplot() +
  geom_sf() +
  theme_grey() +
  theme(axis.text.x=element_text(angle=45, hjust = 1)) +
  theme(legend.position="none") +
  geom_sf(data= argument, aes(fill = DataZone))
}
function7.5 <- function(argument, argument2) 
{
  a <- function1.5(argument)
  b <- function2.5(argument) 
  c <- function5(argument, argument2)
  grid.arrange(a, b, c, nrow = 1)
}
function2.5.1 <- function(argument) 
{
  arransubsect %>%
  ggplot() +
  geom_sf() +
  theme_grey() +
  theme(axis.text.x=element_text(angle=45, hjust = 1)) +
  theme(legend.position="bottom") +
  geom_sf(data= argument, aes(fill = DataZone))
}
function7.5.1 <- function(argument, argument2) 
{
  a <- function1.5(argument)
  b <- function2.5.1(argument) 
  c <- function5(argument, argument2)
  grid.arrange(a, b, c, nrow = 1)
}

Pre-2016

function7.5.1.2 <- function(argument)
{
  function7.5.1(pre2016list[[argument]],pre2016listID[[argument]])
}
lapply(1:7, function7.5.1.2)
[[1]]
TableGrob (1 x 3) "arrange": 3 grobs
  z     cells    name           grob
1 1 (1-1,1-1) arrange gtable[layout]
2 2 (1-1,2-2) arrange gtable[layout]
3 3 (1-1,3-3) arrange gtable[layout]

[[2]]
TableGrob (1 x 3) "arrange": 3 grobs
  z     cells    name           grob
1 1 (1-1,1-1) arrange gtable[layout]
2 2 (1-1,2-2) arrange gtable[layout]
3 3 (1-1,3-3) arrange gtable[layout]

[[3]]
TableGrob (1 x 3) "arrange": 3 grobs
  z     cells    name           grob
1 1 (1-1,1-1) arrange gtable[layout]
2 2 (1-1,2-2) arrange gtable[layout]
3 3 (1-1,3-3) arrange gtable[layout]

[[4]]
TableGrob (1 x 3) "arrange": 3 grobs
  z     cells    name           grob
1 1 (1-1,1-1) arrange gtable[layout]
2 2 (1-1,2-2) arrange gtable[layout]
3 3 (1-1,3-3) arrange gtable[layout]

[[5]]
TableGrob (1 x 3) "arrange": 3 grobs
  z     cells    name           grob
1 1 (1-1,1-1) arrange gtable[layout]
2 2 (1-1,2-2) arrange gtable[layout]
3 3 (1-1,3-3) arrange gtable[layout]

[[6]]
TableGrob (1 x 3) "arrange": 3 grobs
  z     cells    name           grob
1 1 (1-1,1-1) arrange gtable[layout]
2 2 (1-1,2-2) arrange gtable[layout]
3 3 (1-1,3-3) arrange gtable[layout]

[[7]]
TableGrob (1 x 3) "arrange": 3 grobs
  z     cells    name           grob
1 1 (1-1,1-1) arrange gtable[layout]
2 2 (1-1,2-2) arrange gtable[layout]
3 3 (1-1,3-3) arrange gtable[layout]

Post-2016

function7.5.1.1 <- function(argument)
{
  function7.5.1(post2016list[[argument]],post2016listID[[argument]])
}
lapply(1:7, function7.5.1.1)
[[1]]
TableGrob (1 x 3) "arrange": 3 grobs
  z     cells    name           grob
1 1 (1-1,1-1) arrange gtable[layout]
2 2 (1-1,2-2) arrange gtable[layout]
3 3 (1-1,3-3) arrange gtable[layout]

[[2]]
TableGrob (1 x 3) "arrange": 3 grobs
  z     cells    name           grob
1 1 (1-1,1-1) arrange gtable[layout]
2 2 (1-1,2-2) arrange gtable[layout]
3 3 (1-1,3-3) arrange gtable[layout]

[[3]]
TableGrob (1 x 3) "arrange": 3 grobs
  z     cells    name           grob
1 1 (1-1,1-1) arrange gtable[layout]
2 2 (1-1,2-2) arrange gtable[layout]
3 3 (1-1,3-3) arrange gtable[layout]

[[4]]
TableGrob (1 x 3) "arrange": 3 grobs
  z     cells    name           grob
1 1 (1-1,1-1) arrange gtable[layout]
2 2 (1-1,2-2) arrange gtable[layout]
3 3 (1-1,3-3) arrange gtable[layout]

[[5]]
TableGrob (1 x 3) "arrange": 3 grobs
  z     cells    name           grob
1 1 (1-1,1-1) arrange gtable[layout]
2 2 (1-1,2-2) arrange gtable[layout]
3 3 (1-1,3-3) arrange gtable[layout]

[[6]]
TableGrob (1 x 3) "arrange": 3 grobs
  z     cells    name           grob
1 1 (1-1,1-1) arrange gtable[layout]
2 2 (1-1,2-2) arrange gtable[layout]
3 3 (1-1,3-3) arrange gtable[layout]

[[7]]
TableGrob (1 x 3) "arrange": 3 grobs
  z     cells    name           grob
1 1 (1-1,1-1) arrange gtable[layout]
2 2 (1-1,2-2) arrange gtable[layout]
3 3 (1-1,3-3) arrange gtable[layout]

Annotate percentile!

plot text of percentile, at centre of shape file coordinates. overlay postcode labels.

overlay info onto leaflet then with labels.

LS0tCnRpdGxlOiAiQXJyYW4iCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAotLS0KCiNMb2FkIFBhY2thZ2VzCkZpcnN0IEkgbmVlZCB0byBsb2FkIHVwIHRoZSBwYWNrYWdlcyBJJ2xsIG5lZWQKYGBge3J9CmxpYnJhcnkoc2YpCmxpYnJhcnkoZ2dwbG90MikgI2RldmVsb3BtZW50IHZlcnNpb24hCiMjIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigidGlkeXZlcnNlL2dncGxvdDIiKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShyZWFkcikKIyMgTm90IHN1cmUgYWJvdXQgdGhpcyBiaXQKI2xpYnJhcnkoInRpZHl2ZXJzZSIsbGliLmxvYz0iL0xpYnJhcnkvRnJhbWV3b3Jrcy9SLmZyYW1ld29yay9WZXJzaW9ucy8zLjQvUmVzb3VyY2VzL2xpYnJhcnkiKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkoc3ApCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGRwbHlyKQpgYGAKCiNJbXBvcnQgRGF0YQpOb3cgSSBpbXBvcnQgbXkgZGF0YS4gSSBmaWx0ZXIgZm9yIHRoZSBBcnJhbiBwb3N0Y29kZXMsIChzaW5jZSBBcnJhbiBhbGwgYmVnaW5zICdLQTI3JykuCmBgYHtyfQojIyBGaW5kaW5nIHRoZSBBcnJhbiBjb29yZGluYXRlcwphbGxjb29yZGluYXRlcyA8LSByZWFkLmNzdigiYWxsZGF0YS91a3Bvc3Rjb2Rlcy5jc3YiKQphcnJhbmNvb3JkaW5hdGVzIDwtIGZpbHRlcihhbGxjb29yZGluYXRlcyxzdWJzdHIocG9zdGNvZGUsMSw0KT09IktBMjciKQpgYGAKCiNQbG90IENvb3JkaW5hdGVzCk5vdyBJIHBsb3QgdGhlc2UgY29vcmRpbmF0ZXMuCmBgYHtyfQojIyBQbG90dGluZyB0aGUgQXJyYW4gY29vcmRpbmF0ZXMKZ2dwbG90KGRhdGEgPSBhcnJhbmNvb3JkaW5hdGVzKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBsb25naXR1ZGUsIHkgPSBsYXRpdHVkZSksIHNoYXBlPTIwKSArCiAgZ2d0aXRsZSgiQXJyYW4gUG9zdGNvZGVzIikgKwogIGxhYnModGl0bGUgPSAiQXJyYW4gUG9zdGNvZGVzIiwgeCA9ICJMb25naXR1ZGUiLCB5ID0gIkxhdGl0dWRlIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgdGhlbWVfZ3JleSgpICsKICBjb29yZF9tYXAoKQpgYGAKCk5vdyBJIGNyZWF0ZSBzb21lIHBsb3RzLgojQXJyYW4gQm9yZGVycwpgYGB7cn0KcGNzIDwtIHJlYWRfc2YoImFsbGRhdGEvU2NvdGxhbmRfcGNzXzIwMTEiKQoKI1ByaW50IFBvc3QgY29kZXMgbGlzdHMKYXJyYW5zdWJzZWN0IDwtIGZpbHRlcihwY3Msc3Vic3RyKGxhYmVsLDEsNCk9PSJLQTI3IikKYXJyYW5zdWJzZWN0ICU+JQogIGdncGxvdCgpICsKICBnZW9tX3NmKCkgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3QgPSAxKSkgKwogIHRoZW1lX2dyZXkoKQpgYGAKCiNTaW1wbGUuc2YKQWZ0ZXIgYSBsaXR0bGUgZWRpdGluZyBJIGNhbiBvdmVybGF5IHRoZSB0d28uCmBgYHtyfQpzaW1wbGUuc2YgPC0gc3RfYXNfc2YoYXJyYW5jb29yZGluYXRlcywgY29vcmRzPWMoJ2xvbmdpdHVkZScsJ2xhdGl0dWRlJykpCnN0X2NycyhzaW1wbGUuc2YpIDwtIDQzMjYKYGBgCgpgYGB7cn0Kc2ltcGxlLnNmICU+JSAKICBnZ3Bsb3QoKSArIAogIGdlb21fc2Yoc2hhcGU9MjApICsKICB0aGVtZV9ncmV5KCkgCmBgYAoKYGBge3J9CmFycmFuc3Vic2VjdCAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZigpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0ID0gMSkpICsKICB0aGVtZV9ncmV5KCkgKwogIGdlb21fc2YoZGF0YT1zaW1wbGUuc2YsIHNoYXBlPTIwKQpgYGAKCiNTaGFwZSBmaWxlcwpOb3cgSSBsb2FkIHRoZSBTSU1EIGRhdGEsIGNvbnRhaW5pbmcgdGhlIGdlb21ldHJpZXMgKHNoYXBlZmlsZXMpIGFuZCBTSU1EIGRhdGEgKHBlcmNlbnRpbGVzLCBldGMpCmBgYHtyfQojSW1wb3J0IFNJTUQgZGF0YSBmcm9tIGh0dHA6Ly93d3cuZ292LnNjb3QvVG9waWNzL1N0YXRpc3RpY3MvU0lNRAojaHR0cHM6Ly9kYXRhLmdvdi51ay9kYXRhc2V0L3Njb3R0aXNoLWluZGV4LW9mLW11bHRpcGxlLWRlcHJpdmF0aW9uLXNpbWQtMjAxMgojaHR0cHM6Ly9kYXRhLmdvdi51ay9kYXRhc2V0L3Njb3R0aXNoLWluZGV4LW9mLW11bHRpcGxlLWRlcHJpdmF0aW9uLXNpbWQtMjAxMi9yZXNvdXJjZS9kNmZhODkyNC04M2RhLTRlODAtYTU2MC00ZWYwNDc3ZjIzMGIKRFpCb3VuZGFyaWVzMjAxNiA8LSByZWFkX3NmKCIuL2FsbGRhdGEvU0dfU0lNRF8yMDE2IikKRFpCb3VuZGFyaWVzMjAxMiA8LSByZWFkX3NmKCIuL2FsbGRhdGEvU0dfU0lNRF8yMDEyIikKRFpCb3VuZGFyaWVzMjAwOSA8LSByZWFkX3NmKCIuL2FsbGRhdGEvU0dfU0lNRF8yMDA5IikKRFpCb3VuZGFyaWVzMjAwNiA8LSByZWFkX3NmKCIuL2FsbGRhdGEvU0dfU0lNRF8yMDA2IikKRFpCb3VuZGFyaWVzMjAwNCA8LSByZWFkX3NmKCIuL2FsbGRhdGEvU0dfU0lNRF8yMDA0IikKYGBgCgojU2VsZWN0IEFycmFuIFNJTUQgZGF0YQpJIGhhdmUgdG8gY2hvb3NlIHRoZSByaWdodCBjb2x1bW5zIG1hbnVhbGx5IGluIG9yZGVyIHRvIHNlbGVjdCB0aGUgQXJyYW4gZGF0YS4KYGBge3J9CiNTZWxlY3RpbmcgQXJyYW4gZGF0YSBmcm9tIFNjb3RsYW5kICgyMDE2KQojRmluZCBwb3N0Y29kZSBsb29rLXVwIGZyb20gYmVsb3cgZmlsZSBmb3IgS0EyNyBwb3N0Y29kZXMuIEZpbmQgdW5pcXVlIERaLiBGaW5kIHJvdyBwb3NpdGlvbnMuCiNTSU1EMjAxNiA8LXJlYWQuY3N2KCIuL2FsbGRhdGEvMDA1MDUyNDQuY3N2IikKI1NlbGVjdGluZyBBcnJhbkRaMjAxNgpBcnJhbmR6MjAxNiA8LSBjKDQ2NzIsNDY2Niw0NjY5LDQ2NzEsNDY2Nyw0NjY4LDQ2NzApCmFycmFuMjAxNiA8LSBEWkJvdW5kYXJpZXMyMDE2W0FycmFuZHoyMDE2LF0KCiNGaW5kIHBvc3Rjb2RlIGxvb2stdXAsIEtBMjcgcG9zdGNvZGVzLiBGaW5kIHVuaXF1ZSBEWi4gRmluZCByb3cgcG9zaXRpb25zLgojU2VsZWN0aW5nIEFycmFuRFoyMDEyCkFycmFuZHoyMDEyIDwtIGMoNDQwOSw0MzcyLDQzNTMsNDM1Miw0MzUxLDQzNTAsNDM0OSkKCiMyMDEyCmFycmFuMjAxMiA8LSBEWkJvdW5kYXJpZXMyMDEyW0FycmFuZHoyMDEyLF0KIzIwMDkKYXJyYW4yMDA5IDwtIERaQm91bmRhcmllczIwMDlbQXJyYW5kejIwMTIsXQojMjAwNgphcnJhbjIwMDYgPC0gRFpCb3VuZGFyaWVzMjAwNltBcnJhbmR6MjAxMixdCiMyMDA0CmFycmFuMjAwNCA8LSBEWkJvdW5kYXJpZXMyMDA0W0FycmFuZHoyMDEyLF0KYGBgCgpOb3cgSSB3YW50IHRvIHBsb3QgYWxsIHRoZSBkYXRhLCBmaXJzdCBJIGNvbWJpbmUgaXQgYWxsIGludG8gb25lIHRhYmxlLgpGaXJzdCBJIHN1YnNlbGVjdCB0aGUgZGF0YSBJIHdhbnQgZnJvbSB0aGUgYXBwcm9wcmlhdGUgY29sdW1ucy4KYGBge3J9CmFycmFuMjAxNjIgPC0gYXJyYW4yMDE2ICU+JQogIHNlbGVjdChEYXRhWm9uZSwgZ2VvbWV0cnksIFBlcmNlbnRpbGUpICAlPiUKICBtdXRhdGUoeWVhcj0iMjAxNiIpCgphcnJhbjIwMTIyIDwtIGFycmFuMjAxMiAlPiUKICBzZWxlY3QoRGF0YVpvbmUsIGdlb21ldHJ5LCBQZXJjZW50aWxlKSAlPiUKICBtdXRhdGUoeWVhcj0iMjAxMiIpCgphcnJhbjIwMDkyIDwtIGFycmFuMjAwOSAlPiUKICBzZWxlY3QoRGF0YVpvbmUsIGdlb21ldHJ5LCBQZXJjZW50aWxlKSAlPiUKICBtdXRhdGUoeWVhcj0iMjAwOSIpCgphcnJhbjIwMDYyIDwtIGFycmFuMjAwNiAlPiUKICBzZWxlY3QoRGF0YVpvbmUsIGdlb21ldHJ5LCBQZXJjZW50aWxlKSAlPiUKICBtdXRhdGUoeWVhcj0iMjAwNiIpCgphcnJhbjIwMDQyIDwtIGFycmFuMjAwNCAlPiUKICBzZWxlY3QoRGF0YVpvbmUsIGdlb21ldHJ5LCBQZXJjZW50aWxlKSAlPiUKICBtdXRhdGUoeWVhcj0iMjAwNCIpCgphcnJhbnNpbWQgPC0gcmJpbmQoYXJyYW4yMDE2MixhcnJhbjIwMTIyLGFycmFuMjAwOTIsYXJyYW4yMDA2MixhcnJhbjIwMDQyKQpgYGAKClRoZSByZWFzb24gSSd2ZSBkb3dubG9hZGVkIGFsbCB0aGUgZGF0YXpvbmVzIHNoYXBlZmlsZXMgaW5kaXZpZHVhbGx5IGlzIGJlY2F1c2UgdGhleSBjaGFuZ2UgYmV0d2VlbiAyMDE2IGFuZCAyMDEyLgpTZWUgdGhlIHNtYWxsIGRpZmZlcmVuY2VzLgpgYGB7cn0KYXJyYW4xNjEyIDwtIHJiaW5kKGFycmFuMjAxNjIsYXJyYW4yMDEyMikKCmFycmFuMTYxMiAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihhZXMoZmlsbCA9IERhdGFab25lKSkgKwogIGZhY2V0X3dyYXAoJ3llYXInKSArCiAgdGhlbWVfZ3JleSgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdCA9IDEpKQpgYGAKCiNBcnJhbiBQZXJjZW50aWxlIFBsb3RzCk5vdyBJIHBsb3QgdGhlIHBlcmNlbnRpbGVzLgpgYGB7cn0KYXJyYW5zaW1kICU+JQogIGdncGxvdCgpICsKICBnZW9tX3NmKGFlcyhmaWxsID0gUGVyY2VudGlsZSkpICsKICBmYWNldF93cmFwKCd5ZWFyJykgKwogIHRoZW1lX2dyZXkoKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLnk9ZWxlbWVudF9ibGFuaygpKQpgYGAKCmBgYHtyfQphcnJhbnNpbWQgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fc2YoYWVzKGZpbGwgPSBQZXJjZW50aWxlKSkgKwogIGZhY2V0X3dyYXAoJ3llYXInLCBucm93ID0gMSkgKwogIHRoZW1lX2dyZXkoKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLnk9ZWxlbWVudF9ibGFuaygpKQpgYGAKClRoZXJlIHdlIGFyZS4gTm90IHRoZSBTSU1EIGhlYWx0aCBwZXJjZW50aWxlcyBvZiBBcnJhbiB6b25lcyB0aHJvdWdob3V0IFNJTUQgaGlzdG9yeS4gQW5kIEkndmUgbGVhcm5lZCBhIGxpdHRsZSBiaXQgYWJvdXQgZ3JhcGhpY3MgaW4gUi4KCklmIEkgd2FudGVkIHRvIEkgY291bGQgc2hvdyB0aGUgem9uZXMgaW5kaXZpZHVhbGx5Li4KRmlyc3QgSSBmaW5kIHRoZSB1bmlxdWUgem9uZXMuIChUaGVyZSBhcmUgMTQuIDcgWm9uZXMgMjAxNiwgNyBab25lcyBwcmUtMjAxNikKCmBgYHtyfQpkYXRhem9uZXMgPC0gdW5pcXVlKGFycmFuc2ltZCREYXRhWm9uZSkKZGF0YXpvbmVzCmBgYAoKSSdsbCBoYXZlIHRvIGZpbmQgb3V0IGEgc2ltcGxlciB3YXkgdG8gZG8gdGhpcyBidXQuLgojUHJlLTIwMTYgSW5kaXZpZHVhbCBab25lcwpgYGB7cn0KZnVuY3Rpb24wLjUgPC0gZnVuY3Rpb24oYXJndW1lbnQpIAp7CiAgZmlsdGVyKGFycmFuc2ltZCwgRGF0YVpvbmU9PWFyZ3VtZW50KQp9CmBgYAoKI1dheXMgdG8gY3JlYXRlIGluZGl2aWR1YWwgc2hhcGVmaWxlcwpgYGB7cn0KZmlsdGVyKGFycmFuc2ltZCwgRGF0YVpvbmU9PSJTMDEwMDQzNTMiKQpmdW5jdGlvbjAuNSgiUzAxMDA0MzUzIikKYGBgCgpgYGB7cn0KcHJlMjAxNmxpc3QyIDwtIGxpc3QoIlMwMTAwNDQwOSIsICJTMDEwMDQzNzIiLCAiUzAxMDA0MzUzIiwgIlMwMTAwNDM1MiIsICJTMDEwMDQzNTEiLCAiUzAxMDA0MzUwIiwgIlMwMTAwNDM0OSIpCiNDcmVhdGUgYSBuZXcgd2F5IG9mIG1ha2luZyBjaGFyYWN0ZXIgbGlzdApwcmUyMDE2bGlzdCA8LSBsYXBwbHkocHJlMjAxNmxpc3QyLCBmdW5jdGlvbjAuNSkKYGBgCgpgYGB7cn0KZnVuY3Rpb24xIDwtIGZ1bmN0aW9uKGFyZ3VtZW50KSAKewogIGFyZ3VtZW50ICU+JQogIGdncGxvdCgpICsKICBnZW9tX3NmKGFlcyhmaWxsID0gUGVyY2VudGlsZSkpICsKICBmYWNldF93cmFwKCd5ZWFyJykgKwogIHRoZW1lX2dyZXkoKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpCn0KYGBgCgpgYGB7cn0KZnVuY3Rpb24yIDwtIGZ1bmN0aW9uKGFyZ3VtZW50KSAKewogIGFycmFuc3Vic2VjdCAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZigpICsKICB0aGVtZV9ncmV5KCkgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3QgPSAxKSkgKwogIGdlb21fc2YoZGF0YT0gYXJndW1lbnQsIGFlcyhmaWxsID0gRGF0YVpvbmUpKQp9CmBgYAoKYGBge3J9CmZ1bmN0aW9uMyA8LSBmdW5jdGlvbihhcmd1bWVudCkgCnsKICBhcmd1bWVudCAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhID0gYXJyYW5zdWJzZWN0KSArCiAgZ2VvbV9zZihhZXMoZmlsbCA9IFBlcmNlbnRpbGUpKSArCiAgZmFjZXRfd3JhcCgneWVhcicpICsKICB0aGVtZV9ncmV5KCkgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpKQp9CmBgYAoKYGBge3J9CmZ1bmN0aW9uNCA8LSBmdW5jdGlvbihhcmd1bWVudCkgCnsKICBwbG90X2dyaWQoKGZ1bmN0aW9uMShhcmd1bWVudCkpLCAoZnVuY3Rpb24yKGFyZ3VtZW50KSksIGxhYmVscyA9IGMoIkEiLCAiQiIpKQp9CmBgYAoKI0FwcGx5IGZ1bmN0aW9uNCB0byBhbGwgUHJlLTIwMTYuCmBgYHtyfQpsYXBwbHkocHJlMjAxNmxpc3QsIGZ1bmN0aW9uNCkKYGBgCgojMjAxNgpOb3cgSSBhcHBseSB0aGUgc2FtZSBmdW5jdGlvbnMgdG8gdGhlIHBvc3QtMjAxNiBab25lcwpgYGB7cn0KYXJyYW5zdWJzZWN0IDwtIGZpbHRlcihwY3Msc3Vic3RyKGxhYmVsLDEsNCk9PSJLQTI3IikKYGBgCgpgYGB7cn0KI3Bvc3QyMDE2bGlzdCA8LSBsaXN0KFMwMTAxMTE3NywgUzAxMDExMTcxLCBTMDEwMTExNzQsIFMwMTAxMTE3NiwgUzAxMDExMTcyLCBTMDEwMTExNzMsIFMwMTAxMTE3NSkKcG9zdDIwMTZsaXN0MiA8LSBsaXN0KCJTMDEwMTExNzciLCAiUzAxMDExMTcxIiwgIlMwMTAxMTE3NCIsICJTMDEwMTExNzYiLCAiUzAxMDExMTcyIiwgIlMwMTAxMTE3MyIsICJTMDEwMTExNzUiKQpwb3N0MjAxNmxpc3QgPC0gbGFwcGx5KHBvc3QyMDE2bGlzdDIsIGZ1bmN0aW9uMC41KQpgYGAKCmBgYHtyfQpsYXBwbHkocG9zdDIwMTZsaXN0LCBmdW5jdGlvbjQpCmBgYAoKI1Bsb3QgdGhlIHBlcmNlbnRpbGVzIGFzIGJhciBjaGFydHMuClByZS0yMDE2CmBgYHtyfQphcnJhbnNpbWQyIDwtIGRwbHlyOjpmaWx0ZXIoYXJyYW5zaW1kLCB5ZWFyIDwgMjAxNikKCmFycmFuc2ltZDIgJT4lCmdncGxvdChhZXMoeD15ZWFyLCB5PVBlcmNlbnRpbGUpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgZmFjZXRfd3JhcCgnRGF0YVpvbmUnKSArCiAgdGhlbWVfZ3JleSgpICsKICBsYWJzKHRpdGxlID0gIkFycmFuIFNJTUQgRGF0YXpvbmVzIiwgeCA9ICJZZWFyIiwgeSA9ICJQZXJjZW50aWxlIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpgYGAKClBvc3QtMjAxNgpgYGB7cn0KYXJyYW5zaW1kMyA8LSBkcGx5cjo6ZmlsdGVyKGFycmFuc2ltZCwgeWVhciA+PSAyMDE2KQoKYXJyYW5zaW1kMyAlPiUKZ2dwbG90KGFlcyh4PXllYXIsIHk9UGVyY2VudGlsZSkpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpICsKICBmYWNldF93cmFwKCdEYXRhWm9uZScpICsKICB0aGVtZV9ncmV5KCkgKwogIGxhYnModGl0bGUgPSAiQXJyYW4gU0lNRCBEYXRhem9uZXMiLCB4ID0gIlllYXIiLCB5ID0gIlBlcmNlbnRpbGUiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCmBgYAoKTm93IEkgd2FudCB0byBvdmVybGF5IHRoZSBwb3N0Y29kZXMgZm9yIGEgcGFydGljdWxhciBzaGFwZWZpbGUsIGluIHRoaXMgY2FzZSBieSBEYXRhem9uZS4KVG8gZG8gdGhpcyBJJ3ZlIGNvbnZlcnRlZCBib3RoIHRoZSBBcnJhbiBjb29yZGluYXRlcyBhbmQgQXJyYW4gKDIwMTYpIHNoYXBlZmlsZXMgaW50byBTcGF0aWFsIFBvaW50cy9Qb2x5Z29ucywgY29udmVydGVkIHRoZW0gaW50byBhIGNvbW1vbiBDUlMsIGFuZCB0aGVuIGNvbXBhcmVkIHRoZW0gYnkgdXNpbmcgb3ZlcigpLgpgYGB7cn0KZXhhbXBsZXNoYXBlcyA8LSBzZjo6OmFzX1NwYXRpYWwoYXJyYW4yMDE2JGdlb20pCmV4YW1wbGVwb2ludHMgPC0gc2Y6Ojphc19TcGF0aWFsKHNpbXBsZS5zZiRnZW9tKQoKZXhhbXBsZXBvaW50cyA8LSBzcFRyYW5zZm9ybShleGFtcGxlcG9pbnRzLCBDUlMoIitwcm9qPWxvbmdsYXQgK2RhdHVtPVdHUzg0IikpCmV4YW1wbGVzaGFwZXMgPC0gc3BUcmFuc2Zvcm0oZXhhbXBsZXNoYXBlcywgQ1JTKCIrcHJvaj1sb25nbGF0ICtkYXR1bT1XR1M4NCIpKQoKbmFtaW5nZHpwb3N0Y29kZSA8LSBvdmVyKGV4YW1wbGVzaGFwZXMsIGV4YW1wbGVwb2ludHMsIHJldHVybkxpc3QgPSBUUlVFKQpgYGAKCkkgY2FuIHRoZW4gdGFrZSBhIG1lbWJlciByZWZlcmVuY2UgZnJvbSB0aGUgb3JnaW5hbCBwb3N0Y29kZSBsaXN0LCB3aGljaCBnaXZlcyBtZSBhIHNlbGVjdGlvbiBvZiB0aGUgcm93cyBpbiB0aGF0IERaLiBGb3Igc2ltcGxpY2l0eSBJJ3ZlIHdyaXR0ZW4gdGhpcyBhcyBhIG5ldyBmdW5jdGlvbi4gCgpVbmZvcnR1bmF0ZWx5LCBJIGhhdmVuJ3Qgd29ya2VkIG91dCBob3cgdG8gY29vcmRpbmF0ZSB0aGUgbmV3IElEIHdpdGggdGhlIG9yaWdpbmFsIERaIG5hbWVzIHlldCwgc28gSSBoYXZlIHRvIHNlbGVjdCBieSB1c2luZyB0aGUgYXBwcm9wcmlhdGUgSUQgZm9yIGVhY2ggRFouCgpgYGB7cn0KcHJlMjAxNmxpc3RJRCA8LSBsaXN0KDMsMiwxLDQsNyw2LDUpCnBvc3QyMDE2bGlzdElEIDwtIGxpc3QoMSwyLDMsNCw1LDYsNykKYGBgCgojRnVuY3Rpb24gc2VsZWN0aW5nIHNpbXBsZS5zZiBieSBEWiBJRC4KYGBge3J9CmZ1bmN0aW9uNiA8LSBmdW5jdGlvbihhcmd1bWVudCkgCnsKICBzaW1wbGUuc2ZbbmFtaW5nZHpwb3N0Y29kZVtbYXJndW1lbnRdXSxdCn0KYGBgCgpJIGNhbiB0aGVuIHVzZSB0aGUgYWJvdmUsIGFuZCBwbG90IG92ZXIgdGhlIGFwcHJvcHJpYXRlIERaIHNoYXBlZmlsZS4KZS5nCiNQcm9qZWN0aW5nIHRoZSBjb29yZGluYXRlIHNlbGVjdGlvbnMKI25lZWQgdG8gYXV0b21hdGUKYGBge3J9CmZ1bmN0aW9uMShmdW5jdGlvbjAuNSgiUzAxMDA0MzcyIikpICsKICBnZW9tX3NmKGRhdGE9ZnVuY3Rpb242KDIpLCBzaGFwZT0yMCkKZnVuY3Rpb24yKGZ1bmN0aW9uMC41KCJTMDEwMDQzNzIiKSkgKwogIGdlb21fc2YoZGF0YT1mdW5jdGlvbjYoMiksIHNoYXBlPTIwKQpmdW5jdGlvbjMoZnVuY3Rpb24wLjUoIlMwMTAwNDM3MiIpKSArCiAgZ2VvbV9zZihkYXRhPWZ1bmN0aW9uNigyKSwgc2hhcGU9MjApCmBgYAoKSWYgSSBlZGl0IGZ1bmN0aW9uIDQgYSBsaXR0bGUgc28gdGhhdCB0aGUgZ2VvbV9zZiBsYXllciBpcyBhIHNlY29uZCBhcmd1bWVudCB0aGVuIEkgY2FuIGFsc28gdXNlIGZ1bmN0aW9uIDQuCgpgYGB7cn0KZnVuY3Rpb240LjUgPC0gZnVuY3Rpb24oYXJndW1lbnQsIGFyZ3VtZW50MikgCnsKICBhIDwtIGZ1bmN0aW9uMShhcmd1bWVudCkKICBiIDwtIGZ1bmN0aW9uMihhcmd1bWVudCkgKwogIGdlb21fc2YoZGF0YT1mdW5jdGlvbjYoYXJndW1lbnQyKSwgc2hhcGU9MjApCgogIHBsb3RfZ3JpZChhLCBiLCBsYWJlbHMgPSBjKCJBIiwgIkIiKSkKfQpgYGAKCmBgYHtyfQpmdW5jdGlvbjQuNShmdW5jdGlvbjAuNSgiUzAxMDA0MzcyIiksMikKYGBgCgpJJ3ZlIGFsc28gbWFkZSBhbm90aGVyIGZ1bmN0aW9uIHRvIHBsb3QgdGhlIERaIG9uIGl0J3Mgb3duIHdpdGggY29vcmRpbmF0ZXMuCmBgYHtyfQpmdW5jdGlvbjUgPC0gZnVuY3Rpb24oYXJndW1lbnQsIGFyZ3VtZW50MikgCnsKICBhcmd1bWVudCAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZigpICsKICB0aGVtZV9ncmV5KCkgKwogIGdlb21fc2YoZGF0YT1mdW5jdGlvbjYoYXJndW1lbnQyKSwgc2hhcGU9MjApICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSkKfQpgYGAKCmBgYHtyfQpmdW5jdGlvbjUoZnVuY3Rpb24wLjUoIlMwMTAwNDM3MiIpLDIpCmBgYAoKYGBge3J9CmZ1bmN0aW9uMS41IDwtIGZ1bmN0aW9uKGFyZ3VtZW50KSAKewogIGFyZ3VtZW50ICU+JQogIGdncGxvdCgpICsKICBnZW9tX3NmKGFlcyhmaWxsID0gUGVyY2VudGlsZSkpICsKICBmYWNldF93cmFwKCd5ZWFyJykgKwogIHRoZW1lX2dyZXkoKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICAKfQpgYGAKCmBgYHtyfQpmdW5jdGlvbjIuNSA8LSBmdW5jdGlvbihhcmd1bWVudCkgCnsKICBhcnJhbnN1YnNlY3QgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fc2YoKSArCiAgdGhlbWVfZ3JleSgpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0ID0gMSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgZ2VvbV9zZihkYXRhPSBhcmd1bWVudCwgYWVzKGZpbGwgPSBEYXRhWm9uZSkpCn0KYGBgCgpgYGB7cn0KZnVuY3Rpb243LjUgPC0gZnVuY3Rpb24oYXJndW1lbnQsIGFyZ3VtZW50MikgCnsKICBhIDwtIGZ1bmN0aW9uMS41KGFyZ3VtZW50KQogIGIgPC0gZnVuY3Rpb24yLjUoYXJndW1lbnQpIAogIGMgPC0gZnVuY3Rpb241KGFyZ3VtZW50LCBhcmd1bWVudDIpCiAgZ3JpZC5hcnJhbmdlKGEsIGIsIGMsIG5yb3cgPSAxKQp9CmBgYAoKYGBge3J9CmZ1bmN0aW9uMi41LjEgPC0gZnVuY3Rpb24oYXJndW1lbnQpIAp7CiAgYXJyYW5zdWJzZWN0ICU+JQogIGdncGxvdCgpICsKICBnZW9tX3NmKCkgKwogIHRoZW1lX2dyZXkoKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdCA9IDEpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKSArCiAgZ2VvbV9zZihkYXRhPSBhcmd1bWVudCwgYWVzKGZpbGwgPSBEYXRhWm9uZSkpCn0KYGBgCgpgYGB7cn0KZnVuY3Rpb243LjUuMSA8LSBmdW5jdGlvbihhcmd1bWVudCwgYXJndW1lbnQyKSAKewogIGEgPC0gZnVuY3Rpb24xLjUoYXJndW1lbnQpCiAgYiA8LSBmdW5jdGlvbjIuNS4xKGFyZ3VtZW50KSAKICBjIDwtIGZ1bmN0aW9uNShhcmd1bWVudCwgYXJndW1lbnQyKQogIGdyaWQuYXJyYW5nZShhLCBiLCBjLCBucm93ID0gMSkKfQpgYGAKCiNQcmUtMjAxNgpgYGB7cn0KZnVuY3Rpb243LjUuMS4yIDwtIGZ1bmN0aW9uKGFyZ3VtZW50KQp7CiAgZnVuY3Rpb243LjUuMShwcmUyMDE2bGlzdFtbYXJndW1lbnRdXSxwcmUyMDE2bGlzdElEW1thcmd1bWVudF1dKQp9CgpsYXBwbHkoMTo3LCBmdW5jdGlvbjcuNS4xLjIpCmBgYAoKI1Bvc3QtMjAxNgpgYGB7cn0KZnVuY3Rpb243LjUuMS4xIDwtIGZ1bmN0aW9uKGFyZ3VtZW50KQp7CiAgZnVuY3Rpb243LjUuMShwb3N0MjAxNmxpc3RbW2FyZ3VtZW50XV0scG9zdDIwMTZsaXN0SURbW2FyZ3VtZW50XV0pCn0KCmxhcHBseSgxOjcsIGZ1bmN0aW9uNy41LjEuMSkKYGBgCgojQW5ub3RhdGUgcGVyY2VudGlsZSEKcGxvdCB0ZXh0IG9mIHBlcmNlbnRpbGUsIGF0IGNlbnRyZSBvZiBzaGFwZSBmaWxlIGNvb3JkaW5hdGVzLgpvdmVybGF5IHBvc3Rjb2RlIGxhYmVscy4KCm92ZXJsYXkgaW5mbyBvbnRvIGxlYWZsZXQKIHRoZW4gd2l0aCBsYWJlbHMuCg==